﻿using System;
using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;

namespace Gadgeteer.Modules.MSR
{
    /// <summary>
    /// A Button Gadgeteer module.
    /// </summary>
    /// <example>
    /// <para>
    /// The following example shows how to initialize a button and a delegate that handles the button pressed event.
    /// </para>
    /// <code>
    /// using System;
    /// using Microsoft.SPOT;
    /// using Microsoft.SPOT.Presentation;
    /// using Microsoft.SPOT.Presentation.Controls;
    /// using Microsoft.SPOT.Presentation.Media;
    ///
    /// using GT = Gadgeteer;
    /// using GTM = Gadgeteer.Modules;
    /// using Gadgeteer.Modules.MSR;
    ///
    /// namespace GadgeteerButton
    /// {
    ///     public partial class Program
    ///     {
    ///         // Define and initialize GTM.Modules here, specifying their socket numbers.
    ///         GTM.MSR.Button button = new Button(4);
    ///
    ///         static void InitializeMainboard()
    ///         {
    ///             // Specify the mainboard type below.
    ///             // You may need to use the menu item "Project->Add Reference" to add a reference to the mainboard's DLL library.
    ///             Mainboard = new ManufacturerName.Gadgeteer.MainboardName();
    ///         }
    ///
    ///         void ProgramStarted()
    ///         {
    ///             // Initialize event handlers here.
    ///             button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
    ///
    ///             // Do one-time tasks here
    ///             Debug.Print("Program Started");
    ///         }
    ///
    ///         void button_ButtonPressed(Button sender, Button.ButtonState state)
    ///         {
    ///             Debug.Print("Button pressed.");
    ///         }
    ///     }
    /// }
    ///
    /// </code>
    /// </example>
    public class Button : GTM.Module
    {
        // This example implements  a driver in managed code for a simple Gadgeteer module.  The module uses a 
        // single GTI.InterruptInput to interact with a sensor that can be in either of two states: low or high.
        // The example code shows the recommended code pattern for exposing the property (IsHigh). 
        // The example also uses the recommended code pattern for exposing two events: ButtonHigh, ButtonLow. 
        // The triple-slash "///" comments shown will be used in the build process to create an XML file named
        // GTM.ManufacturerName.Button. This file will provide Intellisense and documention for the
        // interface and make it easier for developers to use the Button module.        

        // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        public Button(int socketNumber)
        {
            // This finds the Socket instance from the user-specified socket number.  
            // This will generate user-friendly error messages if the socket is invalid.
            // If there is more than one socket on this module, then instead of "null" for the last parameter, 
            // put text that identifies the socket to the user (e.g. "S" if there is a socket type S)
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);

            // This creates an GTI.InterruptInput interface. The interfaces under the GTI namespace provide easy ways to build common modules.
            // This also generates user-friendly error messages automatically, e.g. if the user chooses a socket incompatible with an interrupt input.
            this.input = new GTI.InterruptInput(socket, GT.Socket.Pin.Three, GTI.GlitchFilterMode.On, GTI.ResistorMode.PullUp, GTI.InterruptMode.RisingAndFallingEdge, this);

            // This registers a handler for the interrupt event of the interrupt input (which is below)
            this.input.Interrupt += new GTI.InterruptInput.InterruptEventHandler(this._input_Interrupt);
        }

        private void _input_Interrupt(GTI.InterruptInput input, bool value)
        {
            this.OnButtonEvent(this, value ? ButtonState.Low : ButtonState.High);
        }

        private GTI.InterruptInput input;

        /// <summary>
        /// Gets a value that indicates whether the state of this Button is high.
        /// </summary>
        public bool IsHigh
        {
            get
            {
                return this.input.Read();
            }
        }

        /// <summary>
        /// Represents the state of the <see cref="Button"/> object.
        /// </summary>
        public enum ButtonState
        {
            /// <summary>
            /// The state of Button is low.
            /// </summary>
            Low = 0,
            /// <summary>
            /// The state of Button is high.
            /// </summary>
            High = 1
        }

        /// <summary>
        /// Represents the delegate that is used to handle the <see cref="ButtonHigh"/>
        /// and <see cref="ButtonLow"/> events.
        /// </summary>
        /// <param name="sender">The <see cref="Button"/> object that raised the event.</param>
        /// <param name="state">The state of the Button</param>
        public delegate void ButtonEventHandler(Button sender, ButtonState state);

        /// <summary>
        /// Raised when the state of <see cref="Button"/> is high.
        /// </summary>
        /// <remarks>
        /// Implement this event handler and the <see cref="ButtonLow"/> event handler
        /// when you want to provide an action associated with Button activity.
        /// The state of the Button is passed to the <see cref="ButtonEventHandler"/> delegate,
        /// so you can use the same event handler for both Button states.
        /// </remarks>
        public event ButtonEventHandler ButtonHigh;

        /// <summary>
        /// Raised when the state of <see cref="Button"/> is low.
        /// </summary>
        /// <remarks>
        /// Implement this event handler and the <see cref="ButtonHigh"/> event handler
        /// when you want to provide an action associated with Button activity.
        /// Since the state of the Button is passed to the <see cref="ButtonEventHandler"/> delegate,
        /// you can use the same event handler for both Button states.
        /// </remarks>
        public event ButtonEventHandler ButtonLow;

        private ButtonEventHandler onButton;

        /// <summary>
        /// Raises the <see cref="ButtonHigh"/> or <see cref="ButtonLow"/> event.
        /// </summary>
        /// <param name="sender">The <see cref="Button"/> that raised the event.</param>
        /// <param name="ButtonState">The state of the Button.</param>
        protected virtual void OnButtonEvent(Button sender, ButtonState ButtonState)
        {
            if (this.onButton == null)
            {
                this.onButton = new ButtonEventHandler(this.OnButtonEvent);
            }

            if (Program.CheckAndInvoke((ButtonState == ButtonState.High ? this.ButtonHigh : this.ButtonLow), this.onButton, sender, ButtonState))
            {
                switch (ButtonState)
                {
                    case ButtonState.High:
                        this.ButtonHigh(sender, ButtonState);
                        break;
                    case ButtonState.Low:
                        this.ButtonLow(sender, ButtonState);
                        break;
                }
            }
        }
    }
}
